package cn.jtfadmin.codegen.service.impl;

import cn.jtfadmin.base.data.jpa.service.JpaService;
import cn.jtfadmin.base.lang.common.domain.dto.EditPDTO;
import cn.jtfadmin.base.lang.common.utils.AkUtils;
import cn.jtfadmin.codegen.domain.dto.*;
import cn.jtfadmin.codegen.domain.entity.CodeGenClassEntity;
import cn.jtfadmin.codegen.domain.entity.CodeGenFieldEntity;
import cn.jtfadmin.codegen.enums.FieldTypeEnum;
import cn.jtfadmin.codegen.enums.InputTypeEnum;
import cn.jtfadmin.codegen.repository.CodeGenClassRepository;
import cn.jtfadmin.codegen.repository.CodegenFieldRepository;
import cn.jtfadmin.codegen.service.CodeGenClassService;
import cn.jtfadmin.codegen.utils.CodeGenUtils;
import cn.jtfadmin.core.modules.sys.domain.dto.SysEnvDTO;
import cn.jtfadmin.core.modules.sys.service.SysEnvActionService;
import org.apache.commons.beanutils.BeanMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;

import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

/**
 * 参数
 */
@Service
@Validated
public class CodeGenClassServiceImpl extends JpaService<CodeGenClassEntity> implements CodeGenClassService {

    @Autowired
    protected CodeGenClassRepository repository;

    @Autowired
    protected CodegenFieldRepository codegenFieldRepository;

    @Autowired
    protected SysEnvActionService sysEnvActionService;


    @Override
    protected JpaRepositoryImplementation<CodeGenClassEntity, String> getJpaRepository() {
        return repository;
    }

    @Override
    protected Class<?> getAddClass() {
        return getEntityClass();
    }

    @Override
    protected Class<? extends EditPDTO> getEditClass() {
        return getEntityClass();
    }

    @Override
    protected CodeGenClassEntity doAdd(Object addPDTO) {
        return doEdit((CodeGenClassEntity)addPDTO);
    }



    @Override
    protected <V extends EditPDTO> CodeGenClassEntity doEdit(V editPDTO) {
        Optional<CodeGenClassEntity>  optional = StringUtils.hasText(editPDTO.getId())?findById(editPDTO.getId()):Optional.empty();
        CodeGenClassEntity entity = null;
        if(optional.isPresent()){
            entity = optional.get();
            List<CodeGenFieldEntity> fields = entity.getFields();
            if(!CollectionUtils.isEmpty(fields)){
                codegenFieldRepository.deleteAll(fields);
            }
            entity.setFields(Collections.emptyList());
            AkUtils.copyProperties(editPDTO,entity);
        }else {
           entity = AkUtils.copyProperties(editPDTO,CodeGenClassEntity.class);
        }
        int i =0;
        for (CodeGenFieldEntity field : entity.getFields()) {
            field.setSort(i++);
            field.setId(null);
            FieldTypeEnum fieldType = field.getFieldType();
            switch (fieldType){
                case ENUM:{
                    field.setLob(false);
                    field.setCollectionClassName(null);
                    field.setOneToMany(false);
                    field.setManyToMany(false);
                    field.setManyToOne(false);
                    field.setOneToOne(false);
                    field.setCascade(null);
                    field.setFetch(null);
                    field.setMappedBy(null);
                    field.setJoinTableName(null);
                    field.setJoinTableIjcName(null);
                    field.setJoinTableJcName(null);
                    field.setJoinColumnName(null);
                    field.setFileNum(null);
                    field.setFileDescribe(null);
                    field.setFileDir(null);
                    field.setFileType(null);
                    field.setMax(null);
                    field.setMin(null);
                    field.setListEmbed(null);
                    field.setSelectEmbed(null);
                    field.setExcelEmbedId(null);
                    field.setModelEmbed(null);
                    field.setExcelEmbed(null);
                    break;
                }
                case BASIC:{
                    field.setCollectionClassName(null);
                    field.setOneToMany(false);
                    field.setManyToMany(false);
                    field.setManyToOne(false);
                    field.setOneToOne(false);
                    field.setCascade(null);
                    field.setFetch(null);
                    field.setMappedBy(null);
                    field.setJoinTableName(null);
                    field.setJoinTableIjcName(null);
                    field.setJoinTableJcName(null);
                    field.setJoinColumnName(null);
                    field.setListEmbed(null);
                    field.setSelectEmbed(null);
                    field.setExcelEmbedId(null);
                    field.setModelEmbed(null);
                    field.setExcelEmbed(null);
                    if(field.getInputType().equals(InputTypeEnum.DATE)
                    ||field.getInputType().equals(InputTypeEnum.DATE_TIME)
                            ||field.getInputType().equals(InputTypeEnum.TIME)
                    ){
                        field.setLob(false);
                        field.setMax(null);
                        field.setMin(null);
                        field.setFileNum(null);
                        field.setFileDescribe(null);
                        field.setFileDir(null);
                        field.setFileType(null);
                    }
                    if(!field.getInputType().equals(InputTypeEnum.File)){
                        field.setFileNum(null);
                        field.setFileDescribe(null);
                        field.setFileDir(null);
                        field.setFileType(null);
                    }
                        break;
                }
                case ENTITY:{
                    if(field.getManyToOne()!=null && field.getManyToOne()){
                        field.setLob(false);
                        field.setCollectionClassName(null);
                        field.setOneToMany(false);
                        field.setManyToMany(false);
                        field.setOneToOne(false);
                        field.setMappedBy(null);
                        field.setJoinTableName(null);
                        field.setJoinTableIjcName(null);
                        field.setJoinTableJcName(null);
                        field.setFileNum(null);
                        field.setFileDescribe(null);
                        field.setFileDir(null);
                        field.setFileType(null);

                        field.setListItem(false);
                        field.setModelItem(false);
                        field.setExcelItem(false);
                        field.setSelectItem(false);
                        field.setExcelId(false);

                        field.setMin(null);
                        field.setMax(null);
                        field.setUnique(null);
                    }else if(field.getOneToOne()!=null && field.getOneToOne()){
                        field.setLob(false);
                        field.setCollectionClassName(null);
                        field.setOneToMany(false);
                        field.setManyToMany(false);
                        field.setManyToOne(false);
                        field.setJoinTableName(null);
                        field.setJoinTableIjcName(null);
                        field.setJoinTableJcName(null);
                        field.setFileNum(null);
                        field.setFileDescribe(null);
                        field.setFileDir(null);
                        field.setFileType(null);

                        field.setListItem(false);
                        field.setModelItem(false);
                        field.setExcelItem(false);
                        field.setSelectItem(false);
                        field.setExcelId(false);

                        field.setMin(null);
                        field.setMax(null);
                        field.setUnique(null);
                    }
                    break;
                }case COLLECTION:{
                    if(field.getManyToMany()!=null && field.getManyToMany()){
                        field.setLob(false);
                        field.setOneToMany(false);
                        field.setOneToOne(false);
                        field.setManyToOne(false);
                        field.setJoinColumnName(null);
                        field.setFileNum(null);
                        field.setFileDescribe(null);
                        field.setFileDir(null);
                        field.setFileType(null);

                        field.setListItem(false);
                        field.setModelItem(false);
                        field.setExcelItem(false);
                        field.setSelectItem(false);
                        field.setExcelId(false);

                        field.setMin(null);
                        field.setMax(null);
                        field.setUnique(null);

                    }else if(field.getOneToMany()!=null && field.getOneToMany()){
                        field.setLob(false);
                        field.setManyToMany(false);
                        field.setOneToOne(false);
                        field.setManyToOne(false);
                        field.setJoinColumnName(null);
                        field.setFileNum(null);
                        field.setFileDescribe(null);
                        field.setFileDir(null);
                        field.setFileType(null);

                        field.setListItem(false);
                        field.setModelItem(false);
                        field.setExcelItem(false);
                        field.setSelectItem(false);
                        field.setExcelId(false);

                        field.setMin(null);
                        field.setMax(null);
                        field.setUnique(null);
                    }
                    break;
                }
            }
        }
        return save(entity);
    }

    private void createFile(Path path) throws IOException {
        if(Files.notExists(path.getParent())){
            Files.createDirectories(path.getParent());
        }
        if(Files.exists(path)){
            Files.delete(path);
        }
    }

    @Override
    public void doGen(String id){
        CodeGenClassEntity entity = findRequiredById(id);
        EntityTempDataDTO entityTempDataDTO = CodeGenUtils.entityToEntityTempData(entity);
        RepositoryTempDataDTO repositoryTempDataDTO = CodeGenUtils.entityToRepositoryTempData(entity);
        ServiceTempDataDTO serviceTempDataDTO = CodeGenUtils.entityToServiceTempData(entity);
        AddTempDataDTO addTempDataDTO = CodeGenUtils.entityToAddTempData(entity);
        EditTempDataDTO editTempDataDTO = CodeGenUtils.entityToEditTempData(entity);
        ExcelTempDataDTO excelTempDataDTO = CodeGenUtils.entityToExcelTempData(entity);
        ServiceImplTempDataDTO serviceImplTempDataDTO = CodeGenUtils.buildServiceImplTempDataDTO(entityTempDataDTO, repositoryTempDataDTO,
                serviceTempDataDTO, addTempDataDTO, editTempDataDTO, excelTempDataDTO, entity);
        ModelTempDataDTO modelTempDataDTO = CodeGenUtils.entityToModelTempData(entity);
        ControllerTempDataDTO controllerTempDataDTO = CodeGenUtils.entityToControllerTempData(entity, serviceTempDataDTO, addTempDataDTO, editTempDataDTO, modelTempDataDTO);
        VueListTemDataDTO vueListTemDataDTO = CodeGenUtils.entityToVueListTemData(entity);
        VueModalTemDataDTO vueMadalTemData = CodeGenUtils.entityToVueMadalTemData(entity);


        ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
        resolver.setCharacterEncoding("utf-8");
        resolver.setTemplateMode(TemplateMode.TEXT);
        TemplateEngine engine = new TemplateEngine();
        engine.setTemplateResolver(resolver);

        String entityStr = engine.process("template/code_gen/entity.temp", new Context(Locale.CHINA, new HashMap((Map)new BeanMap(entityTempDataDTO))));
        String repositoryStr = engine.process("template/code_gen/repository.temp", new Context(Locale.CHINA, new HashMap((Map)new BeanMap(repositoryTempDataDTO))));
        String addStr = engine.process("template/code_gen/addPDTO.temp", new Context(Locale.CHINA, new HashMap((Map)new BeanMap(addTempDataDTO))));
        String editStr = engine.process("template/code_gen/editPDTO.temp", new Context(Locale.CHINA, new HashMap((Map)new BeanMap(editTempDataDTO))));
        String modelStr = engine.process("template/code_gen/modelDTO.temp", new Context(Locale.CHINA, new HashMap((Map)new BeanMap(modelTempDataDTO))));
        String excelStr = engine.process("template/code_gen/excelDTO.temp", new Context(Locale.CHINA, new HashMap((Map)new BeanMap(excelTempDataDTO))));
        String serviceStr = engine.process("template/code_gen/service.temp", new Context(Locale.CHINA, new HashMap((Map)new BeanMap(serviceTempDataDTO))));
        String serviceImplStr = engine.process("template/code_gen/serviceImpl.temp", new Context(Locale.CHINA, new HashMap((Map)new BeanMap(serviceImplTempDataDTO))));
        String controllerStr = engine.process("template/code_gen/controller.temp", new Context(Locale.CHINA, new HashMap((Map)new BeanMap(controllerTempDataDTO))));
        String vueListStr = engine.process("template/code_gen/list.temp", new Context(Locale.CHINA, new HashMap((Map)new BeanMap(vueListTemDataDTO))));
        String vueModalStr = engine.process("template/code_gen/modal.temp", new Context(Locale.CHINA, new HashMap((Map)new BeanMap(vueMadalTemData))));
        String javaHome = null;
        try {
            javaHome = URLDecoder.decode(sysEnvActionService.getValue(JAVA_PROJECT_HOME_KEY), "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        String adminHome = null;
        try {
            adminHome = URLDecoder.decode(sysEnvActionService.getValue(ADMIN_PROJECT_HOME_KEY), "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        String moduleKey = sysEnvActionService.getValue(MODULE_KEY);
        System.out.println(moduleKey);
        Path entityPath = Paths.get(javaHome, moduleKey,"src", "main", "java", entityTempDataDTO.getEntityClass().getClassName().replace(".","/")+".java");
        Path repositoryPath = Paths.get(javaHome, moduleKey,"src", "main", "java", repositoryTempDataDTO.getRepositoryClass().getClassName().replace(".","/")+".java");
        Path addPath = Paths.get(javaHome, moduleKey,"src", "main", "java", addTempDataDTO.getAddClass().getClassName().replace(".","/")+".java");
        Path editPath = Paths.get(javaHome, moduleKey,"src", "main", "java", editTempDataDTO.getEditClass().getClassName().replace(".","/")+".java");
        Path modelPath = Paths.get(javaHome, moduleKey,"src", "main", "java", modelTempDataDTO.getModelClass().getClassName().replace(".","/")+".java");
        Path excelPath = Paths.get(javaHome, moduleKey,"src", "main", "java", excelTempDataDTO.getExcelClass().getClassName().replace(".","/")+".java");
        Path servicePath = Paths.get(javaHome, moduleKey,"src", "main", "java", serviceTempDataDTO.getServiceClass().getClassName().replace(".","/")+".java");
        Path serviceImplPath = Paths.get(javaHome, moduleKey,"src", "main", "java", serviceImplTempDataDTO.getServiceImplClass().getClassName().replace(".","/")+".java");
        Path controllerPath = Paths.get(javaHome, moduleKey,"src", "main", "java", controllerTempDataDTO.getControllerClass().getClassName().replace(".","/")+".java");
        Path vueListPath = Paths.get(adminHome, "src", "views", moduleKey, vueListTemDataDTO.getName()+".vue");
        Path vueModalPath = Paths.get(adminHome, "src", "views", moduleKey, "modal",vueMadalTemData.getName()+".vue");
        Path vueUrlPath = Paths.get(adminHome, "src", "api", "url.js");

        try {
            createFile(entityPath);
            createFile(repositoryPath);
            createFile(addPath);
            createFile(editPath);
            createFile(modelPath);
            createFile(excelPath);
            createFile(servicePath);
            createFile(serviceImplPath);
            createFile(controllerPath);
            createFile(vueListPath);
            createFile(vueModalPath);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            Files.copy(new ByteArrayInputStream(entityStr.getBytes(StandardCharsets.UTF_8)), entityPath);
            Files.copy(new ByteArrayInputStream(repositoryStr.getBytes(StandardCharsets.UTF_8)), repositoryPath);
            Files.copy(new ByteArrayInputStream(addStr.getBytes(StandardCharsets.UTF_8)), addPath);
            Files.copy(new ByteArrayInputStream(editStr.getBytes(StandardCharsets.UTF_8)), editPath);
            Files.copy(new ByteArrayInputStream(modelStr.getBytes(StandardCharsets.UTF_8)), modelPath);
            Files.copy(new ByteArrayInputStream(excelStr.getBytes(StandardCharsets.UTF_8)), excelPath);
            Files.copy(new ByteArrayInputStream(serviceStr.getBytes(StandardCharsets.UTF_8)), servicePath);
            Files.copy(new ByteArrayInputStream(serviceImplStr.getBytes(StandardCharsets.UTF_8)), serviceImplPath);
            Files.copy(new ByteArrayInputStream(controllerStr.getBytes(StandardCharsets.UTF_8)), controllerPath);
            Files.copy(new ByteArrayInputStream(vueListStr.getBytes(StandardCharsets.UTF_8)), vueListPath);
            Files.copy(new ByteArrayInputStream(vueModalStr.getBytes(StandardCharsets.UTF_8)), vueModalPath);
            FileOutputStream fileOutputStream = new FileOutputStream(vueUrlPath.toFile(), true);
            fileOutputStream.write("\r\n".getBytes(StandardCharsets.UTF_8));
            fileOutputStream.write(("export const "+entityTempDataDTO.getEntityClass().getClassSimpleName().replace("Entity","")+"Url"+"={\r\n").getBytes(StandardCharsets.UTF_8));
            fileOutputStream.write(("    list:'"+entity.getUrlPrefix()+"/list', \r\n").getBytes(StandardCharsets.UTF_8));
            fileOutputStream.write(("    add:'"+entity.getUrlPrefix()+"/add', \r\n").getBytes(StandardCharsets.UTF_8));
            fileOutputStream.write(("    edit:'"+entity.getUrlPrefix()+"/edit', \r\n").getBytes(StandardCharsets.UTF_8));
            fileOutputStream.write(("    delete:'"+entity.getUrlPrefix()+"/del', \r\n").getBytes(StandardCharsets.UTF_8));
            fileOutputStream.write(("    deleteBatch:'"+entity.getUrlPrefix()+"/delBatch', \r\n").getBytes(StandardCharsets.UTF_8));
            fileOutputStream.write(("    import:'"+entity.getUrlPrefix()+"/importExcel', \r\n").getBytes(StandardCharsets.UTF_8));
            fileOutputStream.write(("    export:'"+entity.getUrlPrefix()+"/exportExcel', \r\n").getBytes(StandardCharsets.UTF_8));
            fileOutputStream.write(("    named:'"+entity.getUrlPrefix()+"/named', \r\n").getBytes(StandardCharsets.UTF_8));
            if(controllerTempDataDTO.getTreeFlag()){
                fileOutputStream.write(("    tree:'"+entity.getUrlPrefix()+"/tree', \r\n").getBytes(StandardCharsets.UTF_8));
            }
            fileOutputStream.write(("}\r\n").getBytes(StandardCharsets.UTF_8));


        } catch (IOException e) {
            e.printStackTrace();
        }
//
//        SysPermissionAddPDTO dto = new SysPermissionAddPDTO();
//        dto.setName(entity.getComment()+"列表");
//        dto.setComponent("views/"+moduleKey+"/"+vueListTemDataDTO.getName());
//        dto.setUrl("/"+moduleKey+"/"+vueListTemDataDTO.getName());
//        dto.setMenuType(MenuTypeEnum.MENU);
//        dto.setDescription(entity.getComment()+"列表");
//        dto.setRouteFlag(BooleanEnum.TRUE);
//        dto.setHidden(BooleanEnum.FALSE);
//        dto.setKeepAlive(BooleanEnum.FALSE);
//        SysPermissionEntity add = ApplicationHolder.getApplicationContext()
//                .getBean(SysPermissionService.class).add(dto);
//        dto = new SysPermissionAddPDTO();
//        dto.setName(entity.getComment()+"新增");
//        dto.setPerms(vueListTemDataDTO.getAddPerm());
//        dto.setUrl(entity.getUrlPrefix()+"/add");
//        dto.setMenuType(MenuTypeEnum.PERMISSION);
//        dto.setDescription(entity.getComment()+"新增");
//        dto.setOperateType(OperateTypeEnum.ADD);
//        dto.setParentId(add.getId());
//        ApplicationHolder.getApplicationContext()
//                .getBean(SysPermissionService.class).add(dto);
//
//        dto = new SysPermissionAddPDTO();
//        dto.setName(entity.getComment()+"编辑");
//        dto.setPerms(vueListTemDataDTO.getEditPerm());
//        dto.setUrl(entity.getUrlPrefix()+"/edit");
//        dto.setMenuType(MenuTypeEnum.PERMISSION);
//        dto.setDescription(entity.getComment()+"edit");
//        dto.setOperateType(OperateTypeEnum.EDIT);
//        dto.setParentId(add.getId());
//        ApplicationHolder.getApplicationContext()
//                .getBean(SysPermissionService.class).add(dto);
//
//        dto = new SysPermissionAddPDTO();
//        dto.setName(entity.getComment()+"删除");
//        dto.setPerms(vueListTemDataDTO.getDelPerm());
//        dto.setUrl(entity.getUrlPrefix()+"/del,"+entity.getUrlPrefix()+"/delBatch");
//        dto.setMenuType(MenuTypeEnum.PERMISSION);
//        dto.setDescription(entity.getComment()+"删除");
//        dto.setOperateType(OperateTypeEnum.DELETE);
//        dto.setParentId(add.getId());
//        ApplicationHolder.getApplicationContext()
//                .getBean(SysPermissionService.class).add(dto);
//
//        dto = new SysPermissionAddPDTO();
//        dto.setName(entity.getComment()+"导入");
//        dto.setPerms(vueListTemDataDTO.getImportPerm());
//        dto.setUrl(entity.getUrlPrefix()+"/importExcel");
//        dto.setMenuType(MenuTypeEnum.PERMISSION);
//        dto.setDescription(entity.getComment()+"导入");
//        dto.setOperateType(OperateTypeEnum.ADD);
//        dto.setParentId(add.getId());
//        ApplicationHolder.getApplicationContext()
//                .getBean(SysPermissionService.class).add(dto);


    }

    @Override
    public Collection<SysEnvDTO>  getConfig() {
        String basedir = Thread.currentThread().getContextClassLoader().getResource("").getFile();
        if (basedir.contains("!")) {
            basedir = basedir.substring(0, basedir.indexOf("!"));
            basedir = basedir.replace("file:", "");

        }
        File f = new File(basedir).getParentFile();
        String moduleName = "code_gen";
        File java = null;
        if(f.getName().equals("target")){
            java = f.getParentFile().getParentFile();
            moduleName = f.getParentFile().getName();
        }else {
            java = new File(f, "java");
        }
        return Arrays.asList(
                new SysEnvDTO(JAVA_PROJECT_HOME_KEY,java.getAbsolutePath(),"代码生成Java根目录",1),
                new SysEnvDTO(ADMIN_PROJECT_HOME_KEY,new File(java.getParentFile(), "admin").getAbsolutePath(),"代码生成前端根目录",2),
                new SysEnvDTO(MODULE_KEY,moduleName,"代码生成模块名",3)
        );
    }
}
